"""
Schwab API Trading Client

A Python client for interacting with the Schwab Trading API.
Supports account management and order operations.
"""

import requests
import json
from typing import Dict, List, Optional, Any
from datetime import datetime
import logging

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class SchwabAPITradingClientError(Exception):
    """Custom exception for Schwab API errors"""
    def __init__(self, message: str, status_code: int = None, errors: List[str] = None):
        self.message = message
        self.status_code = status_code
        self.errors = errors or []
        super().__init__(self.message)


class SchwabAPITradingClient:
    """
    Schwab Trading API Client
    
    Handles authentication and provides methods for all documented API endpoints.
    """
    
    BASE_URL = "https://api.schwabapi.com/trader/v1"
    
    def __init__(self, access_token: str):
        """
        Initialize the Schwab API client
        
        Args:
            access_token (str): Bearer token for authentication
        """
        self.access_token = access_token
        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': f'Bearer {access_token}',
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        })
    
    def _handle_response(self, response: requests.Response) -> Dict[str, Any]:
        """
        Handle API response and raise appropriate exceptions for errors
        
        Args:
            response: requests.Response object
            
        Returns:
            Dict containing the response data
            
        Raises:
            SchwabAPITradingClientError: For various API error conditions
        """
        correlation_id = response.headers.get('Schwab-Client-CorrelId', 'N/A')
        
        if response.status_code == 200:
            return response.json() if response.content else {}
        elif response.status_code == 201:
            return {
                'status': 'created',
                'location': response.headers.get('Location'),
                'correlation_id': correlation_id
            }
        else:
            try:
                error_data = response.json()
                message = error_data.get('message', f'HTTP {response.status_code} error')
                errors = error_data.get('errors', [])
            except json.JSONDecodeError:
                message = f'HTTP {response.status_code} error'
                errors = []
            
            logger.error(f"API Error {response.status_code}: {message} (Correlation ID: {correlation_id})")
            raise SchwabAPITradingClientError(message, response.status_code, errors)
    
    def get_accounts(self, fields: Optional[str] = None) -> List[Dict[str, Any]]:
        """
        Retrieve linked account(s) balances and positions for the logged-in user.
        
        Args:
            fields (str, optional): Determines which fields to return. 
                                  Possible values: 'positions'
        
        Returns:
            List[Dict]: Array of account objects containing securities account information,
                       balances, and aggregated balance data
        
        Raises:
            SchwabAPITradingClientError: For various API error conditions
        """
        url = f"{self.BASE_URL}/accounts"
        params = {}
        
        if fields:
            params['fields'] = fields
        
        try:
            response = self.session.get(url, params=params)
            return self._handle_response(response)
        except requests.RequestException as e:
            logger.error(f"Request failed: {e}")
            raise SchwabAPITradingClientError(f"Request failed: {e}")
    
    def place_order(self, account_number: str, order: Dict[str, Any]) -> Dict[str, Any]:
        """
        Place an order for a specific account.
        
        Args:
            account_number (str): The encrypted ID of the account
            order (Dict): Complete order object containing order details
        
        Returns:
            Dict: Response containing status, location of created order, and correlation ID
        
        Raises:
            SchwabAPITradingClientError: For various API error conditions
        """
        url = f"{self.BASE_URL}/accounts/{account_number}/orders"
        
        try:
            response = self.session.post(url, json=order)
            return self._handle_response(response)
        except requests.RequestException as e:
            logger.error(f"Request failed: {e}")
            raise SchwabAPITradingClientError(f"Request failed: {e}")
    
    def cancel_order(self, account_number: str, order_id: int) -> Dict[str, Any]:
        """
        Cancel an order for a specific account.
        
        Args:
            account_number (str): The encrypted ID of the account
            order_id (int): The ID of the order being cancelled
        
        Returns:
            Dict: Response indicating successful cancellation
        
        Raises:
            SchwabAPITradingClientError: For various API error conditions
        """
        url = f"{self.BASE_URL}/accounts/{account_number}/orders/{order_id}"
        
        try:
            response = self.session.delete(url)
            return self._handle_response(response)
        except requests.RequestException as e:
            logger.error(f"Request failed: {e}")
            raise SchwabAPITradingClientError(f"Request failed: {e}")
    
    def replace_order(self, account_number: str, order_id: int, order: Dict[str, Any]) -> Dict[str, Any]:
        """
        Replace an existing order for an account.
        
        The existing order will be replaced by the new order. Once replaced, 
        the old order will be canceled and a new order will be created.
        
        Args:
            account_number (str): The encrypted ID of the account
            order_id (int): The ID of the order being replaced
            order (Dict): Complete order object that will replace the existing order
        
        Returns:
            Dict: Response containing status, location of new order, and correlation ID
        
        Raises:
            SchwabAPITradingClientError: For various API error conditions
        """
        url = f"{self.BASE_URL}/accounts/{account_number}/orders/{order_id}"
        
        try:
            response = self.session.put(url, json=order)
            return self._handle_response(response)
        except requests.RequestException as e:
            logger.error(f"Request failed: {e}")
            raise SchwabAPITradingClientError(f"Request failed: {e}")
    
    def get_orders(self, max_results: Optional[int] = None, 
                   from_entered_time: Optional[str] = None,
                   to_entered_time: Optional[str] = None,
                   status: Optional[str] = None) -> List[Dict[str, Any]]:
        """
        Get all orders for all accounts.
        
        Args:
            max_results (int, optional): The max number of orders to retrieve. 
                                       Default is 3000.
            from_entered_time (str, optional): Specifies that no orders entered before 
                                             this time should be returned. Valid ISO-8601 
                                             formats: yyyy-MM-dd'T'HH:mm:ss.SSSZ. 
                                             Date must be within 60 days from today's date. 
                                             'to_entered_time' must also be set.
            to_entered_time (str, optional): Specifies that no orders entered after 
                                           this time should be returned. Valid ISO-8601 
                                           formats: yyyy-MM-dd'T'HH:mm:ss.SSSZ. 
                                           'from_entered_time' must also be set.
            status (str, optional): Specifies that only orders of this status should be returned.
                                  Available values: AWAITING_PARENT_ORDER, AWAITING_CONDITION,
                                  AWAITING_STOP_CONDITION, AWAITING_MANUAL_REVIEW, ACCEPTED,
                                  AWAITING_UR_OUT, PENDING_ACTIVATION, QUEUED, WORKING, REJECTED,
                                  PENDING_CANCEL, CANCELED, PENDING_REPLACE, REPLACED, FILLED,
                                  EXPIRED, NEW, AWAITING_RELEASE_TIME, PENDING_ACKNOWLEDGEMENT,
                                  PENDING_RECALL, UNKNOWN
        
        Returns:
            List[Dict]: List of orders for all linked accounts matching the provided parameters
        
        Raises:
            SchwabAPITradingClientError: For various API error conditions
        """
        url = f"{self.BASE_URL}/orders"
        params = {}
        
        if max_results is not None:
            params['maxResults'] = max_results
        
        if from_entered_time is not None:
            params['fromEnteredTime'] = from_entered_time
        
        if to_entered_time is not None:
            params['toEnteredTime'] = to_entered_time
        
        if status is not None:
            params['status'] = status
        
        # Validate that both time parameters are provided together
        if (from_entered_time is not None) != (to_entered_time is not None):
            raise SchwabAPITradingClientError("Both 'from_entered_time' and 'to_entered_time' must be provided together")
        
        try:
            response = self.session.get(url, params=params)
            return self._handle_response(response)
        except requests.RequestException as e:
            logger.error(f"Request failed: {e}")
            raise SchwabAPITradingClientError(f"Request failed: {e}")


class OrderBuilder:
    """
    Helper class to build order objects for the Schwab API
    """
    
    def __init__(self):
        self.order = {
            "session": "NORMAL",
            "duration": "DAY",
            "orderLegCollection": []
        }
    
    def set_session(self, session: str) -> 'OrderBuilder':
        """Set trading session (NORMAL, AM, PM, SEAMLESS)"""
        self.order["session"] = session
        return self
    
    def set_duration(self, duration: str) -> 'OrderBuilder':
        """Set order duration (DAY, GTC, FOK, IOC)"""
        self.order["duration"] = duration
        return self
    
    def set_order_type(self, order_type: str) -> 'OrderBuilder':
        """Set order type (MARKET, LIMIT, STOP, STOP_LIMIT, etc.)"""
        self.order["orderType"] = order_type
        return self
    
    def set_price(self, price: float) -> 'OrderBuilder':
        """Set limit price for limit orders"""
        self.order["price"] = price
        return self
    
    def set_stop_price(self, stop_price: float) -> 'OrderBuilder':
        """Set stop price for stop orders"""
        self.order["stopPrice"] = stop_price
        return self
    
    def add_equity_leg(self, symbol: str, instruction: str, quantity: int) -> 'OrderBuilder':
        """
        Add an equity leg to the order
        
        Args:
            symbol (str): Stock symbol (e.g., 'AAPL')
            instruction (str): BUY, SELL, BUY_TO_OPEN, SELL_TO_CLOSE, etc.
            quantity (int): Number of shares
        """
        leg = {
            "orderLegType": "EQUITY",
            "instrument": {
                "symbol": symbol
            },
            "instruction": instruction,
            "quantity": quantity
        }
        self.order["orderLegCollection"].append(leg)
        return self
    
    def set_special_instruction(self, instruction: str) -> 'OrderBuilder':
        """Set special instruction (ALL_OR_NONE, etc.)"""
        self.order["specialInstruction"] = instruction
        return self
    
    def build(self) -> Dict[str, Any]:
        """Build and return the order object"""
        return self.order.copy()


# Convenience functions for common order types
def create_market_order(symbol: str, instruction: str, quantity: int, 
                       session: str = "NORMAL", duration: str = "DAY") -> Dict[str, Any]:
    """
    Create a simple market order
    
    Args:
        symbol (str): Stock symbol
        instruction (str): BUY or SELL
        quantity (int): Number of shares
        session (str): Trading session (default: NORMAL)
        duration (str): Order duration (default: DAY)
    
    Returns:
        Dict: Market order object
    """
    return (OrderBuilder()
            .set_session(session)
            .set_duration(duration)
            .set_order_type("MARKET")
            .add_equity_leg(symbol, instruction, quantity)
            .build())


def create_limit_order(symbol: str, instruction: str, quantity: int, price: float,
                      session: str = "NORMAL", duration: str = "GTC") -> Dict[str, Any]:
    """
    Create a limit order
    
    Args:
        symbol (str): Stock symbol
        instruction (str): BUY or SELL
        quantity (int): Number of shares
        price (float): Limit price
        session (str): Trading session (default: NORMAL)
        duration (str): Order duration (default: GTC)
    
    Returns:
        Dict: Limit order object
    """
    return (OrderBuilder()
            .set_session(session)
            .set_duration(duration)
            .set_order_type("LIMIT")
            .set_price(price)
            .add_equity_leg(symbol, instruction, quantity)
            .build())


def create_stop_order(symbol: str, instruction: str, quantity: int, stop_price: float,
                     session: str = "NORMAL", duration: str = "GTC") -> Dict[str, Any]:
    """
    Create a stop order
    
    Args:
        symbol (str): Stock symbol
        instruction (str): BUY or SELL
        quantity (int): Number of shares
        stop_price (float): Stop price
        session (str): Trading session (default: NORMAL)
        duration (str): Order duration (default: GTC)
    
    Returns:
        Dict: Stop order object
    """
    return (OrderBuilder()
            .set_session(session)
            .set_duration(duration)
            .set_order_type("STOP")
            .set_stop_price(stop_price)
            .add_equity_leg(symbol, instruction, quantity)
            .build())


def create_stop_limit_order(symbol: str, instruction: str, quantity: int, 
                           stop_price: float, limit_price: float,
                           session: str = "NORMAL", duration: str = "GTC") -> Dict[str, Any]:
    """
    Create a stop-limit order
    
    Args:
        symbol (str): Stock symbol
        instruction (str): BUY or SELL
        quantity (int): Number of shares
        stop_price (float): Stop price
        limit_price (float): Limit price
        session (str): Trading session (default: NORMAL)
        duration (str): Order duration (default: GTC)
    
    Returns:
        Dict: Stop-limit order object
    """
    return (OrderBuilder()
            .set_session(session)
            .set_duration(duration)
            .set_order_type("STOP_LIMIT")
            .set_stop_price(stop_price)
            .set_price(limit_price)
            .add_equity_leg(symbol, instruction, quantity)
            .build())


# Options Trading Strategy Functions
def create_option_market_buy_order(option_symbol: str, quantity: int, 
                                 session: str = "NORMAL", duration: str = "DAY") -> Dict[str, Any]:
    """
    Create a market order to buy to open an options contract
    
    Args:
        option_symbol (str): Option symbol (e.g., 'XYZ   240315C00500000')
        quantity (int): Number of contracts
        session (str): Trading session (default: NORMAL)
        duration (str): Order duration (default: DAY)
    
    Returns:
        Dict: Market buy to open order object
    """
    return {
        "orderType": "MARKET",
        "session": session,
        "duration": duration,
        "orderStrategyType": "SINGLE",
        "orderLegCollection": [{
            "instruction": "BUY_TO_OPEN",
            "quantity": quantity,
            "instrument": {
                "symbol": option_symbol,
                "assetType": "OPTION"
            }
        }]
    }


def create_option_trailing_stop_order(option_symbol: str, quantity: int, 
                                     trail_offset_percent: float,
                                     current_price: float,
                                     session: str = "NORMAL", 
                                     duration: str = "GTC") -> Dict[str, Any]:
    """
    Create a trailing stop order to sell to close an options position
    
    Args:
        option_symbol (str): Option symbol
        quantity (int): Number of contracts to sell
        trail_offset_percent (float): Trailing stop offset as percentage (e.g., 0.10 for 10%)
        current_price (float): Current option price to calculate offset
        session (str): Trading session (default: NORMAL)
        duration (str): Order duration (default: GTC)
    
    Returns:
        Dict: Trailing stop order object
    """
    trail_offset = current_price * trail_offset_percent
    
    return {
        "complexOrderStrategyType": "NONE",
        "orderType": "TRAILING_STOP",
        "session": session,
        "stopPriceLinkBasis": "BID",
        "stopPriceLinkType": "VALUE",
        "stopPriceOffset": trail_offset,
        "duration": duration,
        "orderStrategyType": "SINGLE",
        "orderLegCollection": [{
            "instruction": "SELL_TO_CLOSE",
            "quantity": quantity,
            "instrument": {
                "symbol": option_symbol,
                "assetType": "OPTION"
            }
        }]
    }


def create_option_stop_limit_order(option_symbol: str, quantity: int,
                                 stop_percent_below: float,
                                 current_price: float,
                                 session: str = "NORMAL",
                                 duration: str = "GTC") -> Dict[str, Any]:
    """
    Create a stop limit order to sell to close an options position
    
    Args:
        option_symbol (str): Option symbol
        quantity (int): Number of contracts to sell
        stop_percent_below (float): Stop percentage below current price (e.g., 0.05 for 5%)
        current_price (float): Current option price
        session (str): Trading session (default: NORMAL)
        duration (str): Order duration (default: GTC)
    
    Returns:
        Dict: Stop limit order object
    """
    stop_price = current_price * (1 - stop_percent_below)
    limit_price = stop_price * 0.95  # Set limit 5% below stop price
    
    return {
        "orderType": "STOP_LIMIT",
        "session": session,
        "price": round(limit_price, 2),
        "stopPrice": round(stop_price, 2),
        "duration": duration,
        "orderStrategyType": "SINGLE",
        "orderLegCollection": [{
            "instruction": "SELL_TO_CLOSE",
            "quantity": quantity,
            "instrument": {
                "symbol": option_symbol,
                "assetType": "OPTION"
            }
        }]
    }


def create_option_oco_exit_orders(option_symbol: str, quantity: int,
                                fill_price: float,
                                trail_offset_percent: float = 0.10,
                                stop_percent_below: float = 0.05,
                                session: str = "NORMAL",
                                duration: str = "GTC") -> Dict[str, Any]:
    """
    Create One Cancels Other (OCO) exit orders for options position
    
    Args:
        option_symbol (str): Option symbol
        quantity (int): Number of contracts
        fill_price (float): Fill price of the original buy order
        trail_offset_percent (float): Trailing stop offset percentage (default: 10%)
        stop_percent_below (float): Stop limit percentage below fill price (default: 5%)
        session (str): Trading session (default: NORMAL)
        duration (str): Order duration (default: GTC)
    
    Returns:
        Dict: OCO order object with trailing stop and stop limit
    """
    trail_offset = fill_price * trail_offset_percent
    stop_price = fill_price * (1 - stop_percent_below)
    limit_price = stop_price * 0.95
    
    return {
        "orderStrategyType": "OCO",
        "childOrderStrategies": [
            {
                "complexOrderStrategyType": "NONE",
                "orderType": "TRAILING_STOP",
                "session": session,
                "stopPriceLinkBasis": "BID",
                "stopPriceLinkType": "VALUE",
                "stopPriceOffset": round(trail_offset, 2),
                "duration": duration,
                "orderStrategyType": "SINGLE",
                "orderLegCollection": [{
                    "instruction": "SELL_TO_CLOSE",
                    "quantity": quantity,
                    "instrument": {
                        "symbol": option_symbol,
                        "assetType": "OPTION"
                    }
                }]
            },
            {
                "orderType": "STOP_LIMIT",
                "session": session,
                "price": round(limit_price, 2),
                "stopPrice": round(stop_price, 2),
                "duration": duration,
                "orderStrategyType": "SINGLE",
                "orderLegCollection": [{
                    "instruction": "SELL_TO_CLOSE",
                    "quantity": quantity,
                    "instrument": {
                        "symbol": option_symbol,
                        "assetType": "OPTION"
                    }
                }]
            }
        ]
    }


def create_option_market_sell_order(option_symbol: str, quantity: int,
                                  session: str = "NORMAL", 
                                  duration: str = "DAY") -> Dict[str, Any]:
    """
    Create a market order to sell to close an options position
    
    Args:
        option_symbol (str): Option symbol
        quantity (int): Number of contracts to sell
        session (str): Trading session (default: NORMAL)
        duration (str): Order duration (default: DAY)
    
    Returns:
        Dict: Market sell to close order object
    """
    return {
        "orderType": "MARKET",
        "session": session,
        "duration": duration,
        "orderStrategyType": "SINGLE",
        "orderLegCollection": [{
            "instruction": "SELL_TO_CLOSE",
            "quantity": quantity,
            "instrument": {
                "symbol": option_symbol,
                "assetType": "OPTION"
            }
        }]
    }


class OptionsTrader:
    """
    High-level options trading strategy manager
    """
    
    def __init__(self, api_client: 'SchwabAPITradingClient'):
        self.client = api_client
        self.active_positions = {}  # Track active positions and their exit orders
    
    def buy_option_with_auto_exits(self, account_number: str, option_symbol: str, 
                                 quantity: int, trail_percent: float = 0.10,
                                 stop_percent: float = 0.05) -> Dict[str, Any]:
        """
        Buy an option and set up automatic exit strategy
        
        This is a two-step process:
        1. Place market buy order
        2. Monitor for fill and then place OCO exit orders
        
        Args:
            account_number (str): Account number
            option_symbol (str): Option symbol
            quantity (int): Number of contracts
            trail_percent (float): Trailing stop percentage (default: 10%)
            stop_percent (float): Stop limit percentage (default: 5%)
        
        Returns:
            Dict: Initial buy order response
        """
        # Create and place the initial market buy order
        buy_order = create_option_market_buy_order(option_symbol, quantity)
        
        try:
            response = self.client.place_order(account_number, buy_order)
            
            # Store position info for later exit order placement
            position_key = f"{account_number}_{option_symbol}"
            self.active_positions[position_key] = {
                'symbol': option_symbol,
                'quantity': quantity,
                'trail_percent': trail_percent,
                'stop_percent': stop_percent,
                'buy_order_id': response.get('correlation_id'),
                'status': 'pending_fill',
                'exit_orders': []
            }
            
            return response
            
        except SchwabAPITradingClientError as e:
            logger.error(f"Failed to place option buy order: {e}")
            raise
    
    def setup_exit_orders(self, account_number: str, option_symbol: str, 
                         fill_price: float) -> Dict[str, Any]:
        """
        Set up OCO exit orders after the initial buy order is filled
        
        Args:
            account_number (str): Account number
            option_symbol (str): Option symbol
            fill_price (float): Fill price from the buy order
        
        Returns:
            Dict: Exit order response
        """
        position_key = f"{account_number}_{option_symbol}"
        
        if position_key not in self.active_positions:
            raise SchwabAPITradingClientError("No active position found for this option")
        
        position = self.active_positions[position_key]
        
        # Create OCO exit orders
        exit_order = create_option_oco_exit_orders(
            option_symbol,
            position['quantity'],
            fill_price,
            position['trail_percent'],
            position['stop_percent']
        )
        
        try:
            response = self.client.place_order(account_number, exit_order)
            
            # Update position with exit order info
            position['exit_orders'].append(response.get('correlation_id'))
            position['status'] = 'exit_orders_placed'
            position['fill_price'] = fill_price
            
            return response
            
        except SchwabAPITradingClientError as e:
            logger.error(f"Failed to place exit orders: {e}")
            raise
    
    def manual_close_position(self, account_number: str, option_symbol: str) -> Dict[str, Any]:
        """
        Manually close an options position and cancel any pending exit orders
        
        Args:
            account_number (str): Account number
            option_symbol (str): Option symbol
        
        Returns:
            Dict: Manual close order response
        """
        position_key = f"{account_number}_{option_symbol}"
        
        if position_key not in self.active_positions:
            raise SchwabAPITradingClientError("No active position found for this option")
        
        position = self.active_positions[position_key]
        
        # Cancel any pending exit orders first
        for exit_order_id in position['exit_orders']:
            try:
                self.client.cancel_order(account_number, exit_order_id)
                logger.info(f"Cancelled exit order {exit_order_id}")
            except SchwabAPITradingClientError as e:
                logger.warning(f"Failed to cancel exit order {exit_order_id}: {e}")
        
        # Place market sell order
        sell_order = create_option_market_sell_order(option_symbol, position['quantity'])
        
        try:
            response = self.client.place_order(account_number, sell_order)
            
            # Update position status
            position['status'] = 'manually_closed'
            
            return response
            
        except SchwabAPITradingClientError as e:
            logger.error(f"Failed to place manual close order: {e}")
            raise
    
    def get_position_status(self, account_number: str, option_symbol: str) -> Dict[str, Any]:
        """
        Get the status of an active position
        
        Args:
            account_number (str): Account number
            option_symbol (str): Option symbol
        
        Returns:
            Dict: Position status information
        """
        position_key = f"{account_number}_{option_symbol}"
        return self.active_positions.get(position_key, {})


# Utility functions
def format_datetime_for_api(dt: datetime) -> str:
    """
    Format a datetime object to ISO-8601 string format required by the Schwab API
    
    Args:
        dt (datetime): datetime object to format
        
    Returns:
        str: ISO-8601 formatted string (yyyy-MM-dd'T'HH:mm:ss.SSSZ)
    """
    return dt.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'


def get_date_range_for_orders(days_back: int = 7) -> tuple:
    """
    Get a date range for querying orders
    
    Args:
        days_back (int): Number of days back from today to start the range
        
    Returns:
        tuple: (from_date_string, to_date_string) in API format
    """
    from datetime import datetime, timedelta
    
    to_date = datetime.now()
    from_date = to_date - timedelta(days=days_back)
    
    return (format_datetime_for_api(from_date), format_datetime_for_api(to_date))


# Example usage and testing functions
def example_usage():
    """
    Example usage of the Schwab API client
    """
    # Initialize client (you'll need a real access token)
    # client = SchwabAPITradingClient("your_access_token_here")
    
    print("Example Schwab API Usage:")
    print("=" * 50)
    
    # Example 1: Get accounts
    print("\n1. Get Accounts:")
    print("client.get_accounts()")
    print("client.get_accounts(fields='positions')")
    
    # Example 2: Get orders
    print("\n2. Get Orders:")
    print("client.get_orders()")
    print("client.get_orders(max_results=100)")
    print("client.get_orders(status='FILLED')")
    
    # Show date range utility
    from_date, to_date = get_date_range_for_orders(days_back=7)
    print(f"client.get_orders(from_entered_time='{from_date}', to_entered_time='{to_date}')")
    
    # Example 3: Create orders
    print("\n3. Create Orders:")
    
    # Market order
    market_order = create_market_order("AAPL", "BUY", 100)
    print(f"Market Order: {json.dumps(market_order, indent=2)}")
    
    # Limit order
    limit_order = create_limit_order("AAPL", "BUY", 100, 150.0)
    print(f"\nLimit Order: {json.dumps(limit_order, indent=2)}")
    
    # Stop order
    stop_order = create_stop_order("AAPL", "SELL", 100, 145.0)
    print(f"\nStop Order: {json.dumps(stop_order, indent=2)}")
    
    # Example 4: Options Trading Strategy
    print("\n4. Options Trading Strategy:")
    
    # Show option symbol format
    option_symbol = "AAPL  240315C00150000"  # AAPL March 15, 2024 $150 Call
    print(f"   Option Symbol: {option_symbol}")
    
    # Market buy order
    option_buy = create_option_market_buy_order(option_symbol, 5)
    print(f"\n   Market Buy Order:")
    print(f"   {json.dumps(option_buy, indent=4)}")
    
    # OCO Exit orders (after fill)
    fill_price = 6.45  # Example fill price
    oco_exits = create_option_oco_exit_orders(option_symbol, 5, fill_price)
    print(f"\n   OCO Exit Orders (10% trail, 5% stop):")
    print(f"   Trail offset: ${fill_price * 0.10:.2f}")
    print(f"   Stop price: ${fill_price * 0.95:.2f}")
    
    # Using the OptionsTrader class
    print(f"\n   Using OptionsTrader Class:")
    print("   trader = OptionsTrader(client)")
    print("   # Step 1: Buy option with auto-exit setup")
    print("   response = trader.buy_option_with_auto_exits('account', 'AAPL  240315C00150000', 5)")
    print("   # Step 2: Setup exits after fill (you'd get fill_price from order status)")
    print("   trader.setup_exit_orders('account', 'AAPL  240315C00150000', fill_price)")
    print("   # Or manually close")
    print("   trader.manual_close_position('account', 'AAPL  240315C00150000')")
    
    # Example 5: Place, modify, cancel orders
    print("\n5. Order Operations:")
    print("# Place order")
    print("response = client.place_order('account_number', market_order)")
    print("\n# Replace order")
    print("response = client.replace_order('account_number', order_id, new_order)")
    print("\n# Cancel order")
    print("response = client.cancel_order('account_number', order_id)")


if __name__ == "__main__":
    example_usage() 